package main

import (
	"database/sql"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strconv"

	"test.com/fusion"
	"test.com/worlddb"
)

type spellLevelIndex struct {
	SpellLevelID uint32 `json:"spellLevelID"`
}

type spellLevelEffectIndex struct {
	SpellLevelEffectID uint32 `json:"spellLevelEffectID"`
	SpellEffectType    uint32 `json:"spellEffectType"`
	SpellSelectType    uint8  `json:"spellSelectType"`
	SpellStageTrigger  uint8  `json:"spellStageTrigger"`
	SpellEffectStyle   uint8  `json:"spellEffectStyle"`
}

type SpellPrototype struct {
	SpellName         string             `json:"spellName"`
	SpellDesc         string             `json:"spellDesc"`
	SpellInfo         *worlddb.SpellInfo `json:"spellInfo"`
	SpellLevelIndexes []*spellLevelIndex `json:"spellLevelIndexes"`
	SpellLevelPrototype
}

type SpellLevelPrototype struct {
	SpellLevelInfo          *worlddb.SpellLevelInfo  `json:"spellLevelInfo"`
	SpellLevelEffectIndexes []*spellLevelEffectIndex `json:"spellLevelEffectIndexes"`
	SpellLevelEffectPrototype
}

type SpellLevelEffectPrototype struct {
	SpellLevelEffectDesc string                        `json:"spellLevelEffectDesc"`
	SpellLevelEffectInfo *worlddb.SpellLevelEffectInfo `json:"spellLevelEffectInfo"`
}

type spellWebPrototype struct {
	SpellPrototype
}

type spellLevelWebPrototype struct {
	SpellLevelIndexes []*spellLevelIndex `json:"spellLevelIndexes"`
	SpellLevelPrototype
}

type spellLevelEffectWebPrototype struct {
	SpellLevelEffectIndexes []*spellLevelEffectIndex `json:"spellLevelEffectIndexes"`
	SpellLevelEffectPrototype
}

func handleGetSpellDialogArgs(w http.ResponseWriter, r *http.Request) {
	scriptFiles, err := getAllScriptable(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	mapNames, err := getAllMapNames(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	tpRows, err := getAllTeleport4DialogTable(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	spellRows, err := getAllSpell4DialogTable(worldDB)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	dialogArgs := map[string]interface{}{
		"scriptFiles": scriptFiles,
		"mapNames":    mapNames,
		"tpRows":      tpRows,
		"spellRows":   spellRows,
	}
	jsonData, err := json.Marshal(dialogArgs)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJsonResponseData(w, jsonData)
}

func handleGetSpellEnumType(w http.ResponseWriter, r *http.Request) {
	enumTypes := map[string]interface{}{
		"ScriptType":      getEnumScriptType(),
		"SpellEffectType": getEnumSpellEffectType(),
	}
	jsonData, err := json.Marshal(enumTypes)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJsonResponseData(w, jsonData)
}

func handleGetSpellUIView(w http.ResponseWriter, r *http.Request) {
	uiViews := map[string]interface{}{
		"SpellResumeType":         getViewSpellResumeType(),
		"SpellTargetType":         getViewSpellTargetType(),
		"SpellPassiveModeCascade": getViewSpellPassiveModeCascade(),
		"SpellInterruptBy":        getViewSpellInterruptBy(),
		"SpellEffectType":         getViewSpellEffectType(),
		"SpellStageType":          getViewSpellStageType(),
		"SpellSelectType":         getViewSpellSelectType(),
		"SpellSelectMode":         getViewSpellSelectMode(),
		"SpellSelectModeArgs":     getViewSpellSelectModeArgs(),
		"SpellEffectStyle":        getViewSpellEffectStyle(),
		"AuraSelectType":          getViewAuraSelectType(),
		"MapType":                 getViewMapType(),
		"AttrArithType":           getViewAttrArithType(),
		"AttrType":                getViewAttrType(),
		"PlayerCareer":            getViewPlayerCareer(),
	}
	jsonData, err := json.Marshal(uiViews)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJsonResponseData(w, jsonData)
}

func handleGetSpellTreeView(w http.ResponseWriter, r *http.Request) {
	handleGetEditorTreeView(
		w, r, worlddb.EditorTreeView_Type_Spell, getAllSpellNames)
}

func handleSaveSpellTreeView(w http.ResponseWriter, r *http.Request) {
	handleSaveEditorTreeView(w, r, worlddb.EditorTreeView_Type_Spell)
}

func handleGetSpellInstance(w http.ResponseWriter, r *http.Request) {
	spellID, err := strconv.ParseUint(r.FormValue("SpellID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	spellData, err := getSpellInstanceJsonData(worldDB, uint32(spellID))
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJsonResponseData(w, spellData)
}

func handleNewSpellInstance(w http.ResponseWriter, r *http.Request) {
	spellName, spellDesc := r.FormValue("SpellName"), r.FormValue("SpellDesc")
	if spellName == "" {
		http.Error(w, "empty spell name.", http.StatusBadRequest)
		return
	}
	var spellID uint32
	err := fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) (err error) {
		spellID, err = newSpellInstance(tx, spellName, spellDesc)
		return
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	spellData, err := getSpellInstanceJsonData(worldDB, spellID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJsonResponseData(w, spellData)
}

func handleCopySpellInstance(w http.ResponseWriter, r *http.Request) {
	spellID, err := strconv.ParseUint(r.FormValue("SpellID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	var ID uint32
	err = fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) (err error) {
		ID, err = copySpellInstance(worldDB, tx, uint32(spellID))
		return
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	spellData, err := getSpellInstanceJsonData(worldDB, ID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeJsonResponseData(w, spellData)
}

func handleDeleteSpellInstance(w http.ResponseWriter, r *http.Request) {
	spellID, err := strconv.ParseUint(r.FormValue("SpellID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	err = fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) error {
		return deleteSpellInstance(tx, uint32(spellID))
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeOKResponseData(w)
}

func handleSaveSpellInstance(w http.ResponseWriter, r *http.Request) {
	spellData, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	var spellProto SpellPrototype
	if err = json.Unmarshal(spellData, &spellProto); err != nil {
		http.Error(w, err.Error(), http.StatusNotAcceptable)
		return
	}
	err = fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) error {
		return saveSpellInstance(tx, &spellProto)
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeOKResponseData(w)
}

func handleGetSpellLevelInstance(w http.ResponseWriter, r *http.Request) {
	spellID, err := strconv.ParseUint(r.FormValue("SpellID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	spellLevelID, err := strconv.ParseUint(r.FormValue("SpellLevelID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	spellData, err := getSpellLevelInstanceJsonData(
		worldDB, uint32(spellID), uint32(spellLevelID))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	writeJsonResponseData(w, spellData)
}

func handleNewSpellLevelInstance(w http.ResponseWriter, r *http.Request) {
	spellID, err := strconv.ParseUint(r.FormValue("SpellID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	var spellLevelID uint32
	err = fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) (err error) {
		spellLevelID, err = newSpellLevelInstance(tx, uint32(spellID))
		return
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	spellData, err := getSpellLevelInstanceJsonData(
		worldDB, uint32(spellID), spellLevelID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	writeJsonResponseData(w, spellData)
}

func handleCopySpellLevelInstance(w http.ResponseWriter, r *http.Request) {
	spellID, err := strconv.ParseUint(r.FormValue("SpellID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	spellLevelID, err := strconv.ParseUint(r.FormValue("SpellLevelID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	var ID uint32
	err = fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) (err error) {
		ID, err = copySpellLevelInstance(worldDB, tx, uint32(spellLevelID))
		return
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	spellData, err := getSpellLevelInstanceJsonData(
		worldDB, uint32(spellID), ID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	writeJsonResponseData(w, spellData)
}

func handleDeleteSpellLevelInstance(w http.ResponseWriter, r *http.Request) {
	spellLevelID, err := strconv.ParseUint(r.FormValue("SpellLevelID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	err = fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) error {
		return deleteSpellLevelInstance(tx, uint32(spellLevelID))
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeOKResponseData(w)
}

func handleGetSpellLevelEffectInstance(w http.ResponseWriter, r *http.Request) {
	spellLevelID, err :=
		strconv.ParseUint(r.FormValue("SpellLevelID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	spellLevelEffectID, err :=
		strconv.ParseUint(r.FormValue("SpellLevelEffectID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	spellData, err := getSpellLevelEffectInstanceJsonData(
		worldDB, uint32(spellLevelID), uint32(spellLevelEffectID))
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	writeJsonResponseData(w, spellData)
}

func handleNewSpellLevelEffectInstance(w http.ResponseWriter, r *http.Request) {
	spellID, err := strconv.ParseUint(r.FormValue("SpellID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	spellLevelID, err := strconv.ParseUint(r.FormValue("SpellLevelID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	var spellLevelEffectID uint32
	err = fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) (err error) {
		spellLevelEffectID, err = newSpellLevelEffectInstance(
			tx, uint32(spellID), uint32(spellLevelID))
		return
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	spellData, err := getSpellLevelEffectInstanceJsonData(
		worldDB, uint32(spellLevelID), spellLevelEffectID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	writeJsonResponseData(w, spellData)
}

func handleCopySpellLevelEffectInstance(w http.ResponseWriter, r *http.Request) {
	spellLevelID, err := strconv.ParseUint(r.FormValue("SpellLevelID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	spellLevelEffectID, err :=
		strconv.ParseUint(r.FormValue("SpellLevelEffectID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	var ID uint32
	err = fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) (err error) {
		ID, err = copySpellLevelEffectInstance(
			worldDB, tx, uint32(spellLevelEffectID))
		return
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	spellData, err := getSpellLevelEffectInstanceJsonData(
		worldDB, uint32(spellLevelID), ID)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	writeJsonResponseData(w, spellData)
}

func handleDeleteSpellLevelEffectInstance(w http.ResponseWriter, r *http.Request) {
	spellLevelEffectID, err :=
		strconv.ParseUint(r.FormValue("SpellLevelEffectID"), 0, 32)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	err = fusion.RunDBTransaction(worldDB, func(tx *sql.Tx) error {
		return deleteSpellLevelEffectInstance(tx, uint32(spellLevelEffectID))
	})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	writeOKResponseData(w)
}
